#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <curl/curl.h>
#include "fetcher.h"
#include "utils.h"

FETCHER *fetcher_init()
{
	FETCHER *fetcher;
	fetcher = (FETCHER *)malloc(sizeof(FETCHER));
	memset(fetcher, 0, sizeof(FETCHER));
	return fetcher;
}

static size_t data2buf(void *ptr, size_t size, size_t nmemb, void *stream)
{
	FETCHER *fetcher;
	size_t realsize;

	realsize = size * nmemb;
	fetcher = (FETCHER *)stream;
	if (fetcher->buffer)
		fetcher->buffer = (char *)realloc(fetcher->buffer, fetcher->size + realsize + 1);
	else
		fetcher->buffer = (char *)malloc(realsize + 1);
	memcpy(fetcher->buffer + fetcher->size, ptr, realsize);
	fetcher->size += realsize;
	fetcher->buffer[fetcher->size] = 0;

	return realsize;
}

char *fetcher_get(FETCHER * fetcher, const char * url)
{
	CURL *curl;
	CURLcode res;
	char realurl[1024];

	replace(url, " ", "%20", realurl);
	curl = curl_easy_init();
	curl_easy_setopt(curl, CURLOPT_URL, realurl);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, data2buf);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)fetcher);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);

	if (res == 0) {
		return fetcher->buffer;
	} else {
		fetcher->size = 0;
		return NULL;
	}
}

void fetcher_clean(FETCHER * fetcher)
{
	if (fetcher->buffer) {
		free(fetcher->buffer);
		fetcher->buffer = 0;
	}
	free(fetcher);
}

static size_t data2file(void *ptr, size_t size, size_t nmemb, void *stream)
{
	int written = fwrite(ptr, size, nmemb, (FILE *)stream);
	return written;
}

/* calculate avarage speed according to the recent 5 status records */
static double get_avg_speed(int curtime, double dlsize)
{
	double totalsize;
	double totaltime;
	int i, lastindex, firstindex;
	static struct speedstack {
		struct speedunit {
			int time;
			double size;
		} su[5];
		int tok;
		int count;
	} ss = {{{0, 0},{0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0};

	/* add current record to speed stack */
	lastindex = ss.tok;
	ss.su[lastindex].time = curtime;
	ss.su[lastindex].size = dlsize;
	ss.tok++;
	ss.tok %= 5;
	if (ss.count < 5) ss.count++;

	/* caculate the avarage speed */
	if (ss.count == 1) {
		return 0;
	} else {
		firstindex = (lastindex + 6 - ss.count) % 5;
		totalsize = ss.su[lastindex].size - ss.su[firstindex].size;
		totaltime = ss.su[lastindex].time - ss.su[firstindex].time;
		return totalsize / totaltime;
	}
}

#define SCREEN_WIDTH	80
#define BAR_WIDTH	30

static int show_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
	SPEEDINFO *psi;
	static char buf[SCREEN_WIDTH];
	static char percent_str[5];
	static char bar_str[BAR_WIDTH+3];
	static char down_str[20];
	static char speed_str[10];
	int i, n;
	int percent, percent_len;
	int buflen = SCREEN_WIDTH;
	static int lasttime = 0;
	static double lastdown = 0;
	int starttime, curtime, dltime;
	double speed;

	curtime = (int)time(0);
	psi = (SPEEDINFO *)clientp;
	psi->lasttime = curtime;
	psi->size = dltotal;

	/* update progress bar per second */
	starttime = psi->starttime;
	if (lasttime == 0) {
		lasttime = starttime;
		lasttime = 0;
	}
	if ((curtime == lasttime) && (dlnow != dltotal)) {
		return 0;
	}

	memset(buf, ' ', buflen - 1);
	buf[buflen - 1] = 0;

	/* draw percentage */
	if (dlnow == dltotal)
		percent = 100;
	else
		percent = (int)(dlnow * 100 / dltotal);
	sprintf(percent_str, "%d%%", percent);
	percent_len = strlen(percent_str);
	strncpy(buf + (4 - percent_len), percent_str, percent_len);

	/* draw progress bar*/
	memset(bar_str, ' ', BAR_WIDTH+2);
	bar_str[BAR_WIDTH+2] = 0;
	bar_str[0] = '[';
	bar_str[BAR_WIDTH+1] = ']';
	n = (int)(percent * BAR_WIDTH / 100);
	for (i=0; i<n-1; i++) {
		bar_str[i+1] = '=';
	}
	if (n < BAR_WIDTH)
		bar_str[i+1] = '>';
	else
		bar_str[i+1] = '=';
	strncpy(buf + 5, bar_str, BAR_WIDTH+2);

	/* size has downloaded */
	strcpy(down_str, "Downloaded: ");
	gen_size_str(down_str+strlen("Downloaded: "), dlnow);
	strncpy(buf + 5 + BAR_WIDTH + 3 + 2, down_str, strlen(down_str));

	/* download speed */
	//dltime = curtime - lasttime;
	if (curtime == starttime) {
		strcpy(speed_str, "--.-- K/s");
	} else {
		//speed = (dlnow - lastdown) / dltime;
		speed = get_avg_speed(curtime, dlnow);
		lastdown = dlnow;
		lasttime = curtime;
		strcpy(speed_str, "--.-- K/s");
		gen_speed_str(speed_str, speed);
	}
	strncpy(buf + SCREEN_WIDTH - strlen(speed_str) - 1, speed_str, strlen(speed_str));

	printf("\r%s", buf);
	fflush(NULL);

	return 0;
}

int fetcher_save(const char * url, FILE * desfile, SPEEDINFO *psi)
{
	CURL *curl;
	CURLcode res;
	char realurl[1024];
	
	psi->starttime = (int)time(0);

	replace(url, " ", "%20", realurl);
	curl = curl_easy_init();
	curl_easy_setopt(curl, CURLOPT_URL, realurl);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, data2file);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)desfile);
	curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
	curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, (curl_progress_callback)show_progress);
	curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *)psi);
	res = curl_easy_perform(curl);
	curl_easy_cleanup(curl);

	return res;	
}

